﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;
using System.Configuration;

using NHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;

using SmartProject.BusinessRulePreferences;
using SmartProject.Entity;

namespace SmartProject.Data
{
    /// <summary>
    /// The DataProvider wraps the functions of the data layer.
    /// </summary>
    public abstract class DataProvider
    {
        private static ISessionFactory _sessionFactory;
        private static DataProvider _instance;
        /// <summary>
        /// Gets a static global instance of the selected DataProvider.
        /// </summary>
        public static DataProvider Instance
        {
            get
            {
                if (_instance == null)
                {
                    Assembly assembly = Assembly.Load(ConfigurationManager.AppSettings["DataProviderAssembly"]);
                    _instance = (DataProvider)assembly.CreateInstance(ConfigurationManager.AppSettings["DataProviderType"]);
                }
                return _instance;
            }
        }

        /// <summary>
        /// Gets the selected connection string stored in App.Config.
        /// </summary>
        protected string ConnectionString
        {
            get
            {
                string name = ConfigurationManager.AppSettings["DataProviderConnectionString"];
                return ConfigurationManager.ConnectionStrings[name].ConnectionString;
            }
        }

        /// <summary>
        /// Creates a new session to execute sql commands.
        /// </summary>
        /// <returns></returns>
        protected abstract ISessionFactory CreateSessionFactory();

        /*
        /// <summary>
        /// Gets or sets if this is the first time SmartProject is being setup.
        /// </summary>
        public static bool IsInitialSetup { get; set; }
        */

        private void Init()
        {
            if (_sessionFactory == null)
            {
                _sessionFactory = CreateSessionFactory();

                // These are data that are initially created by default.
                using (var session = Session)
                {
                    using (var transaction = session.BeginTransaction())
                    {
                        #region Severities
                        if (EntityBase.GetCount(session, typeof(Severity)) == 0)
                        {
                            Severity info = new Severity();
                            info.Description = "Info";
                            info.Level = 50;
                            info.Save(session);

                            Severity debug = new Severity();
                            debug.Description = "Debug";
                            debug.Level = 40;
                            debug.Save(session);

                            Severity warn = new Severity();
                            warn.Description = "Warn";
                            warn.Level = 30;
                            warn.Save(session);

                            Severity error = new Severity();
                            error.Description = "Error";
                            error.Level = 20;
                            error.Save(session);

                            Severity exception = new Severity();
                            exception.Description = "Exception";
                            exception.Level = 10;
                            exception.Save(session);
                        }
                        #endregion

                        #region Permissions/Roles/Users

                        Username username = new Username();
                        if (username.GetSelectedBusinessRule(session) == null)
                        {
                            //IsInitialSetup = true;
                            username.SetSelectedBusinessRule(session, "Simple username");
                        }

                        Permission fullPermissions = null;
                        if (EntityBase.GetCount(session, typeof(Permission)) == 0)
                        {
                            fullPermissions = new Permission();
                            fullPermissions.Action = "FullPermissions";
                            fullPermissions.Save(session);
                        }

                        if (EntityBase.GetCount(session, typeof(Role)) == 0)
                        {
                            Role roleAdmin = new Role();
                            roleAdmin.Name = "Administrator";
                            if (fullPermissions != null)
                            {
                                roleAdmin.Permissions.Add(fullPermissions);
                            }
                            else
                            {
                                fullPermissions = new Permission();
                                roleAdmin.Permissions.Add(Permission.GetPermission(session, "FullPermissions"));
                            }
                            roleAdmin.Save(session);

                            Role roleUser = new Role();
                            roleUser.Name = "User";
                            roleUser.Save(session);
                        }

                        if (EntityBase.GetCount(session, typeof(User)) == 0)
                        {
                            // Create a single default user called Administrator if one does not exist.
                            if (User.GetUserByUsername(session, "Administrator") == null)
                            {
                                User.NewUser(session, "System", "Administrator", "Administrator", "Administrator", Guid.NewGuid().ToString(), new string[1] { "Administrator" });
                            }
                        }
                        #endregion

                        #region File-types
                        // check to see if we can setup the database.
                        if (EntityBase.GetCount(session, typeof(FileType)) == 0)
                        {
                            FileType jpg = new FileType();
                            jpg.Description = "Jpeg image file.";
                            jpg.Extension = "jpg";
                            jpg.Save(session);

                            FileType png = new FileType();
                            png.Description = "Png image file.";
                            png.Extension = "png";
                            png.Save(session);

                            FileType txt = new FileType();
                            txt.Description = "Text file.";
                            txt.Extension = "txt";
                            txt.Save(session);

                            FileType doc = new FileType();
                            doc.Description = "Word document file.";
                            doc.Extension = "doc";
                            doc.Save(session);

                            FileType xml = new FileType();
                            xml.Description = "Xml information file.";
                            xml.Extension = "xml";
                            xml.Save(session);
                        }
                        #endregion

                        #region Priorities
                        if (EntityBase.GetCount(session, typeof(Priority)) == 0)
                        {
                            Priority low = new Priority();
                            low.Description = "Low";
                            low.Level = 50;
                            low.Save(session);

                            Priority normal = new Priority();
                            normal.Description = "Normal";
                            normal.Level = 40;
                            normal.Save(session);

                            Priority high = new Priority();
                            high.Description = "High";
                            high.Level = 30;
                            high.Save(session);

                            Priority important = new Priority();
                            important.Description = "Important";
                            important.Level = 20;
                            important.Save(session);

                            Priority critical = new Priority();
                            critical.Description = "Critical";
                            critical.Level = 10;
                            critical.Save(session);
                        }
                        #endregion

                        #region Task-statuses
                        if (EntityBase.GetCount(session, typeof(TaskStatus)) == 0)
                        {
                            TaskStatus.Add(session, "New");
                            TaskStatus.Add(session, "Assigned");
                            TaskStatus.Add(session, "In Progress");
                            TaskStatus.Add(session, "On Hold");
                            TaskStatus.Add(session, "Internal QA");
                            TaskStatus.Add(session, "Approved");
                        }
                        #endregion

                        if (EntityBase.GetCount(session, typeof(TaskCategory)) == 0)
                        {
                            TaskCategory.Add(session, "Development");
                            TaskCategory.Add(session, "Documentation");
                            TaskCategory.Add(session, "Helpdesk ticket");
                            TaskCategory.Add(session, "Client conference call");
                            TaskCategory.Add(session, "Research");
                        }

                        #region Time-types
                        if (EntityBase.GetCount(session, typeof(TimeType)) == 0)
                        {
                            TimeType min = new TimeType();
                            min.BaseMinutes = 1;
                            min.Description = "Minutes";
                            min.Save(session);

                            TimeType hour = new TimeType();
                            hour.BaseMinutes = 60;
                            hour.Description = "Hour";
                            hour.Save(session);

                            TimeType day = new TimeType();
                            day.BaseMinutes = 8 * 60;
                            day.Description = "Day";
                            day.Save(session);
                        }

                        #endregion

                        transaction.Commit();
                        transaction.Dispose();
                    }
                    session.Close();
                    session.Dispose();
                }
            }
        }

        /// <summary>
        /// Gets the current stateless session of this request.
        /// </summary>
        public IStatelessSession StatelessSession
        {
            get
            {
                Init();
                return _sessionFactory.OpenStatelessSession();
            }
        }

        /// <summary>
        /// Gets the current session of this request.
        /// </summary>
        public ISession Session
        {
            get
            {
                Init();
                return _sessionFactory.OpenSession();
            }
        }

        /// <summary>
        /// Helper function to build a new schema if it is not avaliable.
        /// </summary>
        /// <param name="configuration">NHibernate configuration instance.</param>
        protected static void BuildSchema(NHibernate.Cfg.Configuration configuration)
        {
            NHibernate.Tool.hbm2ddl.SchemaUpdate update =
                new NHibernate.Tool.hbm2ddl.SchemaUpdate(configuration);
            update.Execute(true, true);
        }
    }
}
